/* $begin tinymain */
/*
 * tiny.c - A simple, iterative HTTP/1.0 Web server that uses the 
 *     GET method to serve static and dynamic content.
 */
#include "server.h"

/*
 * doit - handle one HTTP request/response transaction
 */

/*
 * read_requesthdrs - read and parse HTTP request headers
 */
/* $begin read_requesthdrs */
void read_requesthdrs(evbuffer* input, int& content_length) 
{
    size_t len;

    char* line = evbuffer_readln(input, &len, EVBUFFER_EOL_CRLF_STRICT);
    printf("%s\n", line);
    while(strcmp(line, "")) {
	if (strcasestr(line, "content-length")){
		char* ptr = index(line, ':');
		if (ptr)		
			content_length = atoi(ptr+1);
	}
	delete line;
	line = evbuffer_readln(input, &len, EVBUFFER_EOL_CRLF_STRICT);
	printf("%s\n", line);
    }
    return;
}
/* $end read_requesthdrs */

/*
 * parse_uri - parse URI into filename and CGI args
 *             return 0 if dynamic content, 1 if static
 */
/* $begin parse_uri */
bool parse_uri(Method method, char *uri, char *filename, char *cgiargs) 
{
    char *ptr;

    if (!strstr(uri, "cgi_bin")) {  /* Static content */
		strcpy(cgiargs, "");
		strcpy(filename, ".");
		strcat(filename, uri);
		if (uri[strlen(uri)-1] == '/')
			strcat(filename, "home.html");
		return true;
	}
    else {  /* Dynamic content */
		if (method == GET){
			ptr = index(uri, '?');
			if (ptr) {
				strcpy(cgiargs, ptr+1);
				*ptr = '\0';
			}
			else 
			strcpy(cgiargs, "");
		}else strcpy(cgiargs, "");

		strcpy(filename, ".");
		strcat(filename, uri);
		return 0;
    }
}
/* $end parse_uri */

/*
 * serve_static - copy a file back to the client 
 */
/* $begin serve_static */
void serve_static(evbuffer* output, int fd, char *filename, int filesize) 
{
    int srcfd;
    char *srcp, filetype[MAXLINE];
    char* buf = new char[MAXBUF];
 
    /* Send response headers to client */
    get_filetype(filename, filetype);
    sprintf(buf, "HTTP/1.0 200 OK\r\n");
    sprintf(buf, "%sServer: Tiny Web Server\r\n", buf);
    sprintf(buf, "%sContent-length: %d\r\n", buf, filesize);
    sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype);
    //Rio_writen(fd, buf, strlen(buf));

    /* Send response body to client */
    srcfd = Open(filename, O_RDONLY, 0);
    srcp = (char*)Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
    Close(srcfd);
    //sprintf(buf, "%s%s", buf, srcp);

    /*因为有多线程同时访问输出缓冲区，所以加锁进行互斥访问*/
    evbuffer_lock(output);
		
    /*将报文加到输出缓冲区*/
    evbuffer_add(output, buf, strlen(buf));
    evbuffer_add(output, srcp, filesize);
    /*释放锁*/
    evbuffer_unlock(output);
    //Rio_writen(fd, srcp, filesize);
    Munmap(srcp, filesize);
}

/*
 * get_filetype - derive file type from file name
 */
void get_filetype(char *filename, char *filetype) 
{
    if (strstr(filename, ".html"))
	strcpy(filetype, "text/html");
    else if (strstr(filename, ".gif"))
	strcpy(filetype, "image/gif");
    else if (strstr(filename, ".jpg"))
	strcpy(filetype, "image/jpeg");
    else
	strcpy(filetype, "text/plain");
}  
/* $end serve_static */

/*
 * serve_dynamic - run a CGI program on behalf of the client
 */
/* $begin serve_dynamic */
void serve_dynamic(int fd, char *filename, char *cgiargs, 
			Method method, evbuffer* input, int content_length) 
{
    char buf[MAXLINE], *emptylist[] = { NULL };

	if (GET == method){
		if (fork() == 0){
			setenv("QUERY_STRING", cgiargs, 1);
			setenv("REQUEST_METHOD", "GET", 1);
			Dup2(fd, STDOUT_FILENO);         /* Redirect stdout to client */
			Execve(filename, emptylist, environ); /* Run CGI program */
		}
	}else if (POST == method){
		int pipe_fd[2];
		pid_t pid;
		if (pipe(pipe_fd) < 0){
			cerr << "pipe error" << endl;
			return;
		}
		if ((pid = fork()) > 0){
			close(pipe_fd[0]);
			
			size_t len;
			len = evbuffer_remove(input, buf, content_length);
			if (write(pipe_fd[1], buf, len) != len){
			    cerr << "write error to pipe" << endl;
		            return;
			}
			//delete line;	
			close(pipe_fd[1]);
		}else if(0 == pid){
			setenv("REQUEST_METHOD", "POST", 1);
			close(pipe_fd[1]);
			if(pipe_fd[0] != STDIN_FILENO){
				if (dup2(pipe_fd[0], STDIN_FILENO) != STDIN_FILENO){
				  cerr << "dup2 error to stdin" << endl;
				  exit(1);
				}
				close(pipe_fd[0]);
			}
			if (dup2(fd, STDOUT_FILENO) != STDOUT_FILENO){
			  cerr << "dup2 error to stdout" << endl;
			  exit(2);
			}
			Execve(filename, emptylist, environ); /* Run CGI program */
		}
	}
}
/* $end serve_dynamic */

/*
 * clienterror - returns an error message to the client
 */
/* $begin clienterror */
void clienterror(evbuffer* output, int fd, char *cause, char *errnum, 
		 char *shortmsg, char *longmsg) 
{
    char body[MAXBUF];
    char* buf = new char[MAXLINE];

    /* Build the HTTP response body */
    sprintf(body, "<html><title>Tiny Error</title>");
    sprintf(body, "%s<body bgcolor=""ffffff"">\r\n", body);
    sprintf(body, "%s%s: %s\r\n", body, errnum, shortmsg);
    sprintf(body, "%s<p>%s: %s\r\n", body, longmsg, cause);
    sprintf(body, "%s<hr><em>Xiao's web server</em>\r\n", body);

    /* Print the HTTP response */
    sprintf(buf, "HTTP/1.1 %s %s\r\n", errnum, shortmsg);
    //Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "%sContent-type: text/html\r\n", buf);
    //Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "%sContent-length: %d\r\n\r\n%s", buf, (int)strlen(body), body);
    /*因为有多线程同时访问输出缓冲区，所以加锁进行互斥访问*/
    evbuffer_lock(output);
		
    /*将报文加到输出缓冲区*/
    evbuffer_add(output, buf, strlen(buf));

    /*释放锁*/
    evbuffer_unlock(output);
    //Rio_writen(fd, buf, strlen(buf));
    //Rio_writen(fd, body, strlen(body));
}
/* $end clienterror */
